package cs294s.liveradar.service;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;

import cs294s.liveradar.R;
import cs294s.liveradar.data.Blip;

import edu.stanford.concierge.Concierge;

import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.database.DataSetObserver;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;


/*
 * Uses concierge to download blips and pass them to the Map and BlipList.
 */
public class BlipManager {

	/**
	 * Holds the single instance of a that is shared by the process.
	 */
	public static final String LIVERADAR_TAG = "liveradar_blip2";
	private static BlipManager sInstance;
	private Context mContext;

	Concierge concierge;
	String username;
	String password;

	private Builder alertBox;

	ArrayList<Blip> blips = new ArrayList<Blip>();
	long latest_message_id = 0;
	Set<Long> ids_added = new HashSet<Long>();

	/**
	 * Used to post results back to the UI thread
	 */
	private Handler mHandler = new Handler();

	/**
	 * Observers interested in changes to the current list of blips
	 */
	private ArrayList<WeakReference<DataSetObserver>> mObservers = 
		new ArrayList<WeakReference<DataSetObserver>>();


	/**
	 * True if we are in the process of loading
	 */
	private boolean mLoading;

	public static BlipManager getInstance(Context c) {
		if (sInstance == null) {
			sInstance = new BlipManager(c);
		}
		return sInstance;
	}


	private BlipManager(Context c) {
		mContext = c;
		blips = new ArrayList<Blip>();
		alertBox = new AlertDialog.Builder(mContext)
		.setPositiveButton(R.string.alert_dialog_ok, new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int whichButton) {

			}
		});
	}

	public void readPreferences() {
		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
		username = prefs.getString("ConciergeUsername", "");
		password = prefs.getString("ConciergePassword", "");
	}

	/* Check if logged into Concierge */
	public boolean isLoggedIn() {
		return(concierge!=null);
	}

	/*If username and password have changed, then re-login.*/
	public boolean checkLogin() {
		if(isLoggedIn() == false) {
			return loginToConcierge();
		}
		String prev_username = username;
		String prev_password = password;
		readPreferences();
		if(!prev_username.equals(username) || !prev_password.equals(password))
			return loginToConcierge();
		return true;
	}

	public void logoutOfConcierge() {
		concierge = null;
	}

	public boolean loginToConcierge() {
		if(!Util.isNetworkAvailable(mContext)) {

			Toast.makeText(mContext, "Cannot connect to Concierge. Please check internet connectivity", Toast.LENGTH_SHORT).show();
			//showAlert("Cannot connect to Concierge. Please check internet connectivity");
			return false;
		}

		readPreferences();
		if(username.equals("")) {
			showAlert("Welcome to LiveRadar. Please go to Settings and enter your Concierge username and password.");
			return false;
		} 
		Toast.makeText(mContext, "Connecting to Concierge...", Toast.LENGTH_SHORT).show();

		try {	
			//Toast.makeText(mContext, "Connecting to Concierge...", Toast.LENGTH_SHORT).show();

			concierge = new Concierge("http://colby.stanford.edu/community_vibe/concierge/rpc.php", username, password);
			Toast.makeText(mContext, "Connected to Concierge", Toast.LENGTH_SHORT).show();
			
			//showAlert("Logged in to concierge");
		} catch (Exception e) {
			Toast.makeText(mContext, "Unable to connect to Concierge", Toast.LENGTH_SHORT).show();
			//showAlert("Unable to login to Concierge with username: " + username);

			return false;
		}
		return true;
	}

	private void showAlert(String msg) {
		alertBox.setTitle("Concierge Login");
		alertBox.setMessage(msg);
		alertBox.show();
	}


	public boolean sendBlip(Blip blip) {
		if(checkLogin()) {
			blip.addTag(LIVERADAR_TAG);
			concierge.postMessage(blip.toJSON(), blip.tagsToStringArr());
			return true;
		}
		return false;
	}



	/**
	 * Adds an observer to be notified when the set of items held by this BlipManager changes.
	 */
	public synchronized void addObserver(DataSetObserver observer) {
		Log.d("RADAR", "ADD OBSERVER");
		WeakReference<DataSetObserver> obs = new WeakReference<DataSetObserver>(observer);
		mObservers.add(obs);
	}

	public synchronized void removeObserver(DataSetObserver observer) {
		final ArrayList<WeakReference<DataSetObserver>> observers = mObservers;
		final int count = observers.size();
		for (int i = count - 1; i >= 0; i--) {
			WeakReference<DataSetObserver> weak = observers.get(i);
			DataSetObserver obs = weak.get();
			if(obs == observer)
				observers.remove(i);
		}
	}

	/**
	 * Called when something changes in our data set. Cleans up any weak references that
	 * are no longer valid along the way.
	 */
	private synchronized void notifyObservers() {

		Log.d("RADAR", "NOTIFYING OBSERVERS");
		final ArrayList<WeakReference<DataSetObserver>> observers = mObservers;
		final int count = observers.size();
		for (int i = count - 1; i >= 0; i--) {
			WeakReference<DataSetObserver> weak = observers.get(i);
			DataSetObserver obs = weak.get();
			if (obs != null) {
				obs.onChanged();
			} else {
				observers.remove(i);
			}
		}

	}

	public int size() {
		return blips.size();
	}

	public Blip get(int position) {
		return blips.get(position);
	}

	/**
	 * Clear all downloaded content
	 */
	public void clear() {
		blips.clear();   
		notifyObservers();
	}

	/**
	 * @return True if we are still loading content
	 */
	public boolean isLoading() {
		return mLoading;
	}


	/**
	 * Add an item to and notify observers of the change.
	 * @param item The item to add
	 */
	private void add(Blip b) {

		long blip_id = b.getId();
		if(!ids_added.contains(blip_id)){
			ids_added.add(blip_id);
			if(blip_id > latest_message_id)
				latest_message_id = blip_id;
			Log.d("RADAR", "ADDED BLIP: " + blip_id + " TOTAL SIZE: " + size());
			blips.add(b);
		}
	}

	public void downloadBlips() {
		if(checkLogin()) {
			mLoading = true;
			new NetworkThread().start();
		}
	}

	/**
	 * This thread does the actual work of downloading and parsing data.
	 *
	 */
	private class NetworkThread extends Thread {
		public NetworkThread() {

		}

		@Override
		public void run() {
			downloadBlips();
		}

		public void downloadBlips() {
			String[] liveradar_tag_arr = new String[1];
			liveradar_tag_arr[0] = LIVERADAR_TAG;
			JSONArray ja = Concierge.getTaglistStream("http://colby.stanford.edu/community_vibe/concierge/rpc.php", liveradar_tag_arr,  Long.toString(latest_message_id), "10");

			for(int i =ja.size() - 1; i >= 0; i--) {
				try{
					JSONObject j = (JSONObject) ja.get(i);
					long mid = (Long)j.get("mid");
					String content = (String)j.get("content");
					JSONObject blip_json = (JSONObject) JSONValue.parse(content);
					final Blip b = new Blip(blip_json);
					b.setId(mid);
					final boolean done = i == 0;
					mHandler.post(new Runnable() {
						public void run() {
							sInstance.mLoading = !done;
							sInstance.add(b);
						}
					});

				} catch (NullPointerException e) {
					Log.d("RADAR", "CAN'T READ CONCIERGE OBJECT: " + ja.toString());
				}
			}				
			mHandler.post(new Runnable() {
				public void run() {
					sInstance.notifyObservers();
				}
			});

		}

	}

}
